/*
 * Copyright Radosław Smogura, 2012. All rights reserved.
 *
 * The content of this file is confidential and it is intelectual property
 * of copyright owners.
 * 
 *
 * $URL: http://reprap-ide.googlecode.com/svn/trunk/RepRapIDE/RepRapProject/src/main/java/eu/softper/reprap/generator/DecimalVertex.java $
 * $Id: DecimalVertex.java 3 2012-11-19 10:58:30Z mail@smogura.eu $
 * $Revision: 3 $
 */
package eu.softper.reprap.generator;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Comparator;

/**
 *
 * @author Radosław Smogura
 */
public class DecimalVertex<T extends Number & Comparable<T>> implements Cloneable {

    private static final DecimalFormat TO_STRING_FORMAT = new DecimalFormat("#0.000");
    private final T x, y, z;

    public DecimalVertex(T x, T y, T z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public T getX() {
        return x;
    }

    public T getY() {
        return y;
    }

    public T getZ() {
        return z;
    }

    public static class ZIndexComparator<T extends Number & Comparable<T>> implements Comparator<DecimalVertex<T>> {

        @Override
        public int compare(DecimalVertex<T> o1, DecimalVertex<T> o2) {
            return o1.getZ().compareTo(o2.getZ());
        }
    }

    public static class DictionaryComparator<T extends Number & Comparable<T>> implements Comparator<DecimalVertex<T>> {

        public static final DictionaryComparator SHARED_INSTANCE = new DictionaryComparator();

        @Override
        public int compare(DecimalVertex<T> o1, DecimalVertex<T> o2) {
            int cmp;

            cmp = o1.getX().compareTo(o2.getX());
            if (cmp != 0) {
                return cmp;
            }

            cmp = o1.getY().compareTo(o2.getY());
            if (cmp != 0) {
                return cmp;
            }

            return o1.getZ().compareTo(o2.getY());
        }
    }

    @Override
    protected DecimalVertex clone() {
        try {
            return (DecimalVertex) super.clone();
        } catch (CloneNotSupportedException cnse) {
            throw new java.lang.Error("It should not happen.", cnse);
        }
    }

    @Override
    public String toString() {
        return '[' + TO_STRING_FORMAT.format(x)
                + "; " + TO_STRING_FORMAT.format(y)
                + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final DecimalVertex<T> other = (DecimalVertex<T>) obj;
        if (this.x != other.x && (this.x == null || !(this.x.compareTo(other.x) == 0))) {
            return false;
        }
        if (this.y != other.y && (this.y == null || !(this.y.compareTo(other.y) == 0))) {
            return false;
        }
        if (this.z != other.z && (this.z == null || !(this.z.compareTo(other.z) == 0))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 17 * hash + (this.x != null ? this.x.hashCode() : 0);
        hash = 17 * hash + (this.y != null ? this.y.hashCode() : 0);
        hash = 17 * hash + (this.z != null ? this.z.hashCode() : 0);
        return hash;
    }

    private T toT(double val) {
        T c = getX();
        if (c instanceof Double) {
            Double double1 = (Double) c;
            return (T) Double.valueOf(val);
        }else if (c instanceof BigDecimal) {
            BigDecimal bigDecimal = (BigDecimal) c;
            return (T) BigDecimal.valueOf(val);
        }else {
            throw new IllegalStateException();
        }
    }
    ///////////////////////////////////////////////////////////////////////////
    // Transformation methods for ur purposes
    //
    
    public DecimalVertex<T> intersectWithPlane(DecimalVertex<T> v1, DecimalVertex<T> v2, T zPos) {
        /*
         * To keep same results when interseting we order vertices accorfing to
         * dictionary order. This is because computer math is not symbolic math,
         * and it uses rounding so e.g (A + B) / C != (A/C) + (B/C).
         *
         * Line equation: v = v1 + t(v2 - v1) -> t = (v - v1) / (v2 - v1)
         */
        if (DecimalVertex.DictionaryComparator.SHARED_INSTANCE.compare(v1, v2) > 0) {
            DecimalVertex tmpVert = v1;
            v1 = v2;
            v2 = tmpVert;
        }
        
        /* Because we use here division there is no need to keep decimal format. */
        //First code
        //BigDecimal v1Neg = v1.getZ().negate();
        //BigDecimal t = zPos.add(v1Neg).divide(v2.getZ().add(v1Neg), SCALE, RoundingMode.HALF_UP);
        //return verticesCache.intern(new DecimalVertex(
        //        v1.getX().add(t.multiply(v2.getX().add(v1.getX().negate()))),
        //        v1.getY().add(t.multiply(v2.getY().add(v1.getY().negate()))),
        //        zPos;
        
        double v1Neg = -v1.getZ().doubleValue();
        double t = (zPos.doubleValue() + v1Neg) / (v2.getZ().doubleValue() + v1Neg);
        
        double x = v1.getX().doubleValue() + 
                t * (v2.getX().doubleValue() - v1.getX().doubleValue());
        double y = v1.getY().doubleValue() + 
                t * (v2.getY().doubleValue() - v1.getY().doubleValue());
                
        return  new DecimalVertex<T>(toT(x), toT(y), zPos);
    }
}
